#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/socket.h>

#include <errno.h>
#include <string.h>

#include "pb.h"
#include "pb_decode.h"
#include "pb_encode.h"

#include "somb.h"
#include "somb_daemon.pb.h"

#include "somb_common.h"
enum
{
    somb_cmd_invalid,
    somb_cmd_show_status,
}somb_cmd;

static char *ns_str=NULL;
int daemon_skfd = -1;
bool gDetail_flag=false;

int somb_ctrl_connect_peer(int *skfd,char *PeerAddr)
{
    if((NULL == skfd)||(NULL == PeerAddr))
    {
        return -1; 
    }

    if(*skfd == -1)
    {
        *skfd =  socket(AF_UNIX,SOCK_STREAM|SOCK_CLOEXEC,0);

        struct sockaddr_un tmp_un_addr;
        memset(&(tmp_un_addr),0,sizeof(tmp_un_addr));
        tmp_un_addr.sun_family = AF_UNIX;
        snprintf(tmp_un_addr.sun_path,sizeof(tmp_un_addr.sun_path),"%s",SOMB_CTRL_ADDR);

        unlink(SOMB_CTRL_ADDR);
        if(bind(*skfd ,(struct sockaddr *)&tmp_un_addr,SUN_LEN(&tmp_un_addr))<0)
        {
            printf("somb-ctrl bind addr(%s) failed\r\n",SOMB_CTRL_ADDR);
            return -1;
        }


        struct sockaddr_un ServerAddr_un;
        memset(&(ServerAddr_un),0,sizeof(ServerAddr_un));
        ServerAddr_un.sun_family = AF_UNIX;
        snprintf(ServerAddr_un.sun_path,sizeof(ServerAddr_un.sun_path),"%s",PeerAddr);
        if(connect(*skfd,(struct sockaddr*)&ServerAddr_un,(socklen_t)SUN_LEN(&ServerAddr_un)) < 0)
        {
            printf("somb-ctrl: connect peer(%s) failed(%s)\r\n",PeerAddr,strerror(errno));
            return -1;
        }
    }

    return 0;
}

void show_help(int cmd,somb_reg_sub_ns *RegSubNsList)
{
    printf("somb-ctrl -n NAMESPACE_NAME {COMMNAD}\r\n");
    printf("NAMESPACE_NAME:{daemon | ");
    int loop  = 0;
    for(;loop < RegSubNsList->reg_ns_list_count;loop++)
    {
        printf("%s ",RegSubNsList->reg_ns_list[loop].sub_ns_name);
        if((loop !=0)&&(loop%7 == 0))
        {
            printf("\t\n");
        }
    }
    printf("}\t\n");

    printf("\r\n");
    printf("COMMAND:{-s}\r\n");
    printf("\r-s: get the status for namespace\r\n");
}

int somb_ctrl_recv_msg_from_daemon(int skfd,void *msgreq,const pb_msgdesc_t * reqfields,uint32_t reqfield_size)
{
    int ret ;

    uint8_t *buffer = malloc(SOMB_DAEMON_MSG_HEADER_SIZE+reqfield_size);
    if(NULL == buffer)
    {
        printf("somb-ctrl malloc recv buffer,errno=%s\r\n",strerror(errno));
        return -1;
    }

    ret = recvfrom(skfd,buffer,SOMB_DAEMON_MSG_HEADER_SIZE+reqfield_size,0,NULL,NULL);
    if(ret <= 0)
    {
        printf("somb-ctrl recv from daeomn,errno=%s\r\n",strerror(errno));
        return ret;
    }
    //uint32_t msgid = *(uint32_t *)buffer;
    
    pb_istream_t istream = pb_istream_from_buffer(&(buffer[SOMB_DAEMON_MSG_HEADER_SIZE]),ret - SOMB_DAEMON_MSG_HEADER_SIZE);
    if(false == pb_decode(&istream,reqfields, msgreq))
    {
        printf("somb-ctrl recv from daeomn,decode errno=%s %s\r\n",strerror(errno),PB_GET_ERROR(&istream));
        return -2;
    }
    
    free(buffer);
    return 0;
}
int somb_ctrl_send_msg(int skfd,uint32_t MsgId, void *msgreq,const pb_msgdesc_t * reqfields,uint32_t reqfield_size)
{
    int send_len = 0;
    uint8_t *buffer=malloc(SOMB_DAEMON_MSG_HEADER_SIZE+reqfield_size);
    if(NULL == buffer)
    {
        printf("somb-ctrl malloc for req msg(%d) failed\r\n",MsgId);
        return SOMB_FAIL;
    }
    memset(buffer,0,SOMB_DAEMON_MSG_HEADER_SIZE+reqfield_size);
    pb_ostream_t stream;
    if(NULL != reqfields)
    {
        stream = pb_ostream_from_buffer(&(buffer[SOMB_DAEMON_MSG_HEADER_SIZE]), reqfield_size);
        bool status = pb_encode(&stream, reqfields, msgreq);
        if(!status)
        {
            printf("somb-ctrl encode msg(%d) failed\r\n",MsgId);
            return SOMB_MSG_ENCODE_ERR;
        }
        send_len = SOMB_DAEMON_MSG_HEADER_SIZE+stream.bytes_written;
    }
    else
    {
        send_len = SOMB_DAEMON_MSG_HEADER_SIZE;
        stream.bytes_written=0;
    }

    *(uint32_t*)buffer = MsgId;
    *(uint32_t*)(buffer+4) = stream.bytes_written;

    int32_t len = send(skfd,buffer,send_len,MSG_DONTWAIT);
    if(len != send_len)
    {
        printf("somb-ctrl send msg(%d)  failed,ret=%d errno=%s\r\n",MsgId,len,strerror(errno));
        return SOMB_FAIL;
    }

    free(buffer);
    return 0;

}

void do_cmd_show_status_daemon(somb_reg_sub_ns *RegSubNsList)
{
    printf("SOMB Registered SubNs:(Total=%d)\r\n",RegSubNsList->reg_ns_list_count);
    int loop = 0,k=0;
    for(;loop < RegSubNsList->reg_ns_list_count;loop++)
    {
        printf("\n\t|->%s\n",RegSubNsList->reg_ns_list[loop].sub_ns_name);
        printf("\t|-->SubNsId=%d Pid=%d Status=%s\n", RegSubNsList->reg_ns_list[loop].sub_ns_id,
                RegSubNsList->reg_ns_list[loop].sub_ns_pid,\
                RegSubNsList->reg_ns_list[loop].sub_ns_status==1?"online":"offline");
        printf("\t|-->Owned ServiceList:(Total=%d)\n",RegSubNsList->reg_ns_list[loop].service_info_count);
        for(k=0;k<RegSubNsList->reg_ns_list[loop].service_info_count;k++)
        {
            printf("\t|--->0x%08x ",RegSubNsList->reg_ns_list[loop].service_info[k].service_id);
            printf("%s ",RegSubNsList->reg_ns_list[loop].service_info[k].service_type==SOMB_SVC_TYPE_METHOD?"Method":"Event");
            printf("%s\n",RegSubNsList->reg_ns_list[loop].service_info[k].service_status?"OK":"NG");
        }
    }
}

void do_cmd_show_status_subns(somb_reg_sub_ns *RegSubNsList)
{
    int loop = 0;
    int32_t sub_ns_id = 0;
    char dest_addr[128];
    for(;loop < RegSubNsList->reg_ns_list_count;loop++)
    {
        if(strcmp(ns_str,RegSubNsList->reg_ns_list[loop].sub_ns_name) == 0)
        {
            sub_ns_id = RegSubNsList->reg_ns_list[loop].sub_ns_id;
            sprintf(dest_addr,SOMB_SUB_NS_ADDR_FMT1, RegSubNsList->reg_ns_list[loop].sub_ns_id);
            break;
        }
    }

    if(0  ==  sub_ns_id)
    {
        printf("the namespce[%s] is not recongized\r\n",ns_str);
        return ;
    }

    int peer_skfd = -1;
    if(0 !=somb_ctrl_connect_peer(&peer_skfd,dest_addr))
    {
        printf("somb-ctrl connect to peer(%s) failed\n",dest_addr);
        return ;
    }

    int ret = somb_ctrl_send_msg(peer_skfd,somb_daemon_msgid_ctrl_get_sub_ns_status, NULL,NULL,0);
    if(0 !=  ret)
    {
        printf("get subns status for namespce[%s] failed1\r\n",ns_str);
        return ;
    }
    printf("waiting from subns(%s)\n",dest_addr);
    somb_subns_work_status msgresp1 = somb_subns_work_status_init_zero;
    if(0 != somb_ctrl_recv_msg_from_daemon(peer_skfd,&msgresp1,somb_subns_work_status_fields,somb_subns_work_status_size))
    {
        printf("get subns status for namespce[%s] failed2\r\n",ns_str);
        return ;
    }
    printf("\n\nSubNs(%s) service statistic:\r\n",ns_str);
    for(loop=0;loop < msgresp1.service_statistic_count;loop++)
    {
        printf("ServiceId:0x%08x",msgresp1.service_statistic[loop].service_id);
        printf("\tRole:%s",msgresp1.service_statistic[loop].service_role==0?"local":"remote");
        printf("\tType:%s\n",msgresp1.service_statistic[loop].service_type==SOMB_SVC_TYPE_EVENT?"event":"method");
        if(msgresp1.service_statistic[loop].service_role==0)
        {
            if(msgresp1.service_statistic[loop].service_type==SOMB_SVC_TYPE_EVENT)
            {
                printf("\tActive Triggering:%d\n",msgresp1.service_statistic[loop].Count);
                printf("\tActive Triggering(Failed):%d\n",msgresp1.service_statistic[loop].FailCount);
            }
            else
            {
                printf("\tPassive Requested:%d\n",msgresp1.service_statistic[loop].Count);
                printf("\tPassive Requested(Failed):%d\n",msgresp1.service_statistic[loop].FailCount);
            }
        }
        else
        {
            if(msgresp1.service_statistic[loop].service_type==SOMB_SVC_TYPE_EVENT)
            {
                printf("\tPassive Triggered:%d\n",msgresp1.service_statistic[loop].Count);
                printf("\tPassive Triggered(Failed):%d\n",msgresp1.service_statistic[loop].FailCount);
            }
            else
            {
                printf("\tActive Request:%d\n",msgresp1.service_statistic[loop].Count);
                printf("\tActive Requested(Failed):%d\n",msgresp1.service_statistic[loop].FailCount);
            }
        }
    }

    printf("SubNs(%s) work status:\r\n",ns_str);
    for(loop=0;loop < msgresp1.work_job_last_time_count;loop++)
    {
        uint32_t *time = (uint32_t *)&(msgresp1.work_job_last_time[loop]) ;
        printf("work%d:\r\n",loop+1);
        printf("\tlast_job_time:%d.%06d\n",time[0],time[1]);
        printf("\tjob_count:%d\n",msgresp1.work_job_num[loop]);
    }
    printf("\r\n");
}

void do_cmd(int cmd,somb_reg_sub_ns *RegSubNsList)
{
    switch(cmd)
    {
        case somb_cmd_show_status:
            {
                if(strcmp(ns_str,"daemon") == 0)
                {
                    do_cmd_show_status_daemon(RegSubNsList);
                }
                else
                {
                    do_cmd_show_status_subns(RegSubNsList);
                }
            }
            break;
        default:
            printf("unsupported cmd %d\r\n",cmd);
            show_help(0,RegSubNsList);
            break;
        
    }
}
int main(int argc, char *argv[])
{
    char *optstr="n:sdh";
    int cmd = 0;
    bool bshow_help = false;

    /*check somb-daemon whether was in running*/
    if(0 != access(SOMB_DAEMON_ADDR,F_OK))
    {
        printf("somb-ctrl is not in running, check somb-daemon's status\r\n");
        return -1;
    }

    somb_reg_sub_ns RegSubNsList = somb_reg_sub_ns_init_zero;
    memset(&RegSubNsList,0,sizeof(RegSubNsList));

    if(0 !=somb_ctrl_connect_peer(&daemon_skfd,SOMB_DAEMON_ADDR))
    {
        printf("somb-ctrl connect to daemon failed\n");
        return -1;
    }
    somb_ctrl_send_msg(daemon_skfd,somb_daemon_msgid_get_reg_sub_ns_list,NULL,NULL,0);
    //waiting response
    if(0 != somb_ctrl_recv_msg_from_daemon(daemon_skfd,&RegSubNsList,somb_reg_sub_ns_fields,somb_reg_sub_ns_size))
    {
        return -2;
    }
    char c;
    while ((c = getopt (argc, argv, optstr)) != -1)
    switch (c)
      {
      case 'n':
          ns_str = optarg;
        break;
      case 's':
        cmd = somb_cmd_show_status;
        break;
      case 'd':
        gDetail_flag = true;
        break;
      case 'h':
        bshow_help = true;
        break;
      default:
        abort ();
      }
    
    if(bshow_help)
    {
        show_help(cmd,&RegSubNsList);
        return 0;
    }

    do_cmd(cmd,&RegSubNsList);
    return 0;
}
